#include "ModbusMasterProxy.h"

ModbusMasterProxy::ModbusMasterProxy(QObject *parent) : QObject(parent), ctx(NULL), _respCount(0), _pollCount(0), _isBusy(false)
{

}

ModbusMasterProxy::~ModbusMasterProxy()
{

}

void ModbusMasterProxy::clearCount()
{
    _respCount = 0;
    _pollCount = 0;
}

uint32_t ModbusMasterProxy::respCount()
{
    return _respCount;
}

uint32_t ModbusMasterProxy::pollCount()
{
    return _pollCount;
}

void ModbusMasterProxy::setProxy(modbus_t **ctx)
{
    this->ctx = ctx;
}

bool ModbusMasterProxy::isConnected()
{
    if(*ctx == NULL)
        return false;
    if((*ctx)->backend->backend_type == _MODBUS_BACKEND_TYPE_TCP)
        return modbus_get_socket(*ctx) < 0 ? false : true;
    return true;
}

bool ModbusMasterProxy::isBusy()
{
    return _isBusy;
}

void ModbusMasterProxy::read(quint8 slaveAddress, quint8 functionCode, quint16 startAddress, quint32 numToRead, quint32 responseTimeout)
{
    QByteArray data;
    uint16_t value[2000];

    if(!isConnected())
    {
        return;
    }

    _isBusy = true;

    _pollCount++;

    if(modbus_get_slave(*ctx) != slaveAddress)
    {
        modbus_set_slave(*ctx, slaveAddress);
    }

    responseTimeout *= 1000;
    modbus_set_response_timeout(*ctx, responseTimeout / 1000000, responseTimeout % 1000000);  // 设置超时时间

    modbus_flush(*ctx);

    switch(functionCode)
    {

    case 0X01:  // 读线圈
    {
        int offset    = startAddress;
        int totalRead = numToRead;

        while(totalRead != 0)
        {
            int maxToRead = totalRead >= MODBUS_MAX_READ_BITS ? MODBUS_MAX_READ_BITS : totalRead;
            if(modbus_read_bits(*ctx, offset, maxToRead, (uint8_t *)value) < 0)
            {
                goto __failed;
            }
            data      += QByteArray((const char *)value, maxToRead);
            offset    += maxToRead;
            totalRead -= maxToRead;
        }

        break;
    }

    case 0X02:  // 读离散量输入
    {
        int offset    = startAddress;
        int totalRead = numToRead;

        while(totalRead != 0)
        {
            int maxToRead = totalRead >= MODBUS_MAX_READ_BITS ? MODBUS_MAX_READ_BITS : totalRead;
            if(modbus_read_input_bits(*ctx, offset, maxToRead, (uint8_t *)value) < 0)
            {
                goto __failed;
            }
            data      += QByteArray((const char *)value, maxToRead);
            offset    += maxToRead;
            totalRead -= maxToRead;
        }

        break;
    }

    case 0X03:  // 读保持寄存器
    {
        int offset    = startAddress;
        int totalRead = numToRead;

        while(totalRead != 0)
        {
            int maxToRead = totalRead >= MODBUS_MAX_READ_REGISTERS ? MODBUS_MAX_READ_REGISTERS : totalRead;
            if(modbus_read_registers(*ctx, offset, maxToRead, value) < 0)
            {
                goto __failed;
            }
            data      += QByteArray((const char *)value, maxToRead * 2);
            offset    += maxToRead;
            totalRead -= maxToRead;
        }

        break;
    }

    case 0X04:  // 读输入寄存器
    {
        int offset    = startAddress;
        int totalRead = numToRead;

        while(totalRead != 0)
        {
            int maxToRead = totalRead >= MODBUS_MAX_READ_REGISTERS ? MODBUS_MAX_READ_REGISTERS : totalRead;
            if(modbus_read_input_registers(*ctx, offset, maxToRead, value) < 0)
            {
                goto __failed;
            }
            data      += QByteArray((const char *)value, maxToRead * 2);
            offset    += maxToRead;
            totalRead -= maxToRead;
        }

        break;
    }

    default:
        break;
    }

    goto __exit;

__failed:
    data.clear();

__exit:
    if(data.size() != 0)
    {
        _respCount++;
    }
    emit this->response(slaveAddress, functionCode, startAddress, numToRead, data);
    _isBusy = false;
}

void ModbusMasterProxy::write(quint8 slaveAddress, quint8 functionCode, quint16 startAddress, quint32 numToWrite, QByteArray data,  quint32 responseTimeout)
{
    if(!isConnected())
    {
        return;
    }

    _isBusy = true;

    _pollCount++;

    if(modbus_get_slave(*ctx) != slaveAddress)
    {
        modbus_set_slave(*ctx, slaveAddress);
    }

    responseTimeout *= 1000;
    modbus_set_response_timeout(*ctx, responseTimeout / 1000000, responseTimeout % 1000000);  // 设置超时时间

    modbus_flush(*ctx);

    switch(functionCode)
    {

    case 0X05:  // 写单个线圈
    {
        quint16 value = data[0];
        if(modbus_write_bit(*ctx, startAddress, value) < 0)
        {
            goto __failed;
        }
        break;
    }

    case 0X06:  // 写单个寄存器
    {
        quint16 value = ((quint8)data[1] << 8) | (quint8)data[0];
        if(modbus_write_register(*ctx, startAddress, value) < 0)
        {
            goto __failed;
        }
        break;
    }

    case 0X0F:  // 写多个线圈
    {
        const uint8_t* pBuffer = (const uint8_t *)data.data();
        quint16 offset = startAddress;
        while(numToWrite != 0)
        {
            int maxToWrite = numToWrite >= MODBUS_MAX_WRITE_BITS ? MODBUS_MAX_WRITE_BITS : numToWrite;
            if(modbus_write_bits(*ctx, offset, maxToWrite, pBuffer) < 0)
            {
                goto __failed;
            }
            numToWrite -= maxToWrite;
            pBuffer    += maxToWrite;
            offset     += maxToWrite;
        }
        break;
    }

    case 0X10:  // 写多个寄存器
    {
        const uint16_t* pBuffer = (const uint16_t* )data.data();
        quint16 offset = startAddress;
        while(numToWrite != 0)
        {
            int maxToWrite = numToWrite >= MODBUS_MAX_WRITE_REGISTERS ? MODBUS_MAX_WRITE_REGISTERS : numToWrite;                     // 一次最多写入123个寄存器位
            if(modbus_write_registers(*ctx, offset, maxToWrite, pBuffer) < 0)
            {
                goto __failed;
            }
            numToWrite -= maxToWrite;
            pBuffer    += maxToWrite;
            offset     += maxToWrite;
        }
        break;
    }

    default:break;
    }

    goto __exit;

__failed:
    data.clear();

__exit:
    if(data.size() != 0)
    {
        _respCount++;
    }
    emit this->response(slaveAddress, functionCode, startAddress, numToWrite, data);
    _isBusy = false;
}
